//: C25:Recycle2.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 1999
// Copyright notice in Copyright.txt
// Adding a factory method
#include "sumValue.h"
#include "../purge.h"
#include <fstream>
#include <vector>
#include <typeinfo>
#include <cstdlib>
#include <ctime>
using namespace std;
ofstream out("Recycle2.out");

class Trash
{
    double _weight;
    void operator=(const Trash&);
    Trash(const Trash&);
public:
    Trash(double wt) : _weight(wt) { }
    virtual double value() const = 0;
    double weight() const {
        return _weight;
    }
    virtual ~Trash() {}
    // Nested class because it's tightly coupled
    // to Trash:
    class Info
    {
        int type;
        // Must change this to add another type:
        static const int maxnum = 3;
        double data;
        friend class Trash;
    public:
        Info(int typeNum, double dat)
                : type(typeNum % maxnum), data(dat) {}
    };
    static Trash* factory(const Info& info);
};

class Aluminum : public Trash
{
    static double val;
public:
    Aluminum(double wt) : Trash(wt) {}
    double value() const {
        return val;
    }
    static void value(double newval) {
        val = newval;
    }
    ~Aluminum() {
        out << "~Aluminum\n";
    }
};

double Aluminum::val = 1.67F;

class Paper : public Trash
{
    static double val;
public:
    Paper(double wt) : Trash(wt) {}
    double value() const {
        return val;
    }
    static void value(double newval) {
        val = newval;
    }
    ~Paper() {
        out << "~Paper\n";
    }
};

double Paper::val = 0.10F;

class Glass : public Trash
{
    static double val;
public:
    Glass(double wt) : Trash(wt) {}
    double value() const {
        return val;
    }
    static void value(double newval) {
        val = newval;
    }
    ~Glass() {
        out << "~Glass\n";
    }
};

double Glass::val = 0.23F;

// Definition of the factory method. It must know
// all the types, so is defined after all the
// subtypes are defined:
Trash* Trash::factory(const Info& info)
{
    switch (info.type) {
    default: // In case of overrun
    case 0:
        return new Aluminum(info.data);
    case 1:
        return new Paper(info.data);
    case 2:
        return new Glass(info.data);
    }
}

// Generator for Info objects:
class InfoGen
{
    int typeQuantity;
    int maxWeight;
public:
    InfoGen(int typeQuant, int maxWt)
            : typeQuantity(typeQuant), maxWeight(maxWt) {
        srand(time(0));
    }
    Trash::Info operator()() {
        return Trash::Info(rand() % typeQuantity,
                           static_cast<double>(rand() % maxWeight));
    }
};

int main()
{
    vector<Trash*> bin;
    // Fill up the Trash bin:
    InfoGen infoGen(3, 100);
    for (int i = 0; i < 30; i++)
        bin.push_back(Trash::factory(infoGen()));
    vector<Aluminum*> alBin;
    vector<Paper*> paperBin;
    vector<Glass*> glassBin;
    vector<Trash*>::iterator sorter = bin.begin();
    // Sort the Trash:
    while (sorter != bin.end()) {
        Aluminum* ap =
            dynamic_cast<Aluminum*>(*sorter);
        Paper* pp = dynamic_cast<Paper*>(*sorter);
        Glass* gp = dynamic_cast<Glass*>(*sorter);
        if (ap) alBin.push_back(ap);
        if (pp) paperBin.push_back(pp);
        if (gp) glassBin.push_back(gp);
        sorter++;
    }
    sumValue(alBin);
    sumValue(paperBin);
    sumValue(glassBin);
    sumValue(bin);
    purge(bin); // Cleanup
} ///:~
